package controllers;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;

import play.Play;
import play.exceptions.UnexpectedException;
import play.libs.WS;
import play.libs.WS.WSRequest;
import play.libs.ws.WSUrlFetch;
import play.mvc.Controller;
import play.mvc.Router;
import play.mvc.Scope.Session;
import play.modules.m4fcity.M4FCityProfile;
	
/**
 * The Class M4FCityController.
 * 
 * @author David Basoko (david.basoko@m4f.es)
 * @copyright m4f (http://m4f.es)
 */
public class M4FCityController extends Controller {
	
	public static void login() {
    	String url = String.format("%s?client_id=%s&redirect_uri=%s&response_type=code&scope", getM4FCityOauthAuthorizeUrl(), WS.encode(getApiKey()), WS.encode(getOauthRedirectUrl()));
        redirect(url);
	}
	
	public static void logout() {
		Session.current().put("username", null);
		redirect(getLandUrl());
	}
	
	public static void callback() {
		String code = params.get("code");
        String error = params.get("error");
        
        if(error != null){
            String landUrl = getLandUrl(); // TODO
            landUrl += landUrl.contains("?") ? "&" : "?";
            landUrl += "error_reason=" + WS.encode(params.get("error_reason")) +
                        "&error_description=" + WS.encode(params.get("error_description")) +
                        "&error=" + WS.encode(params.get("error"));
            redirect(landUrl);
        }
        if(code != null){
            String authUrl = getAuthUrl(code);
           // System.out.println(authUrl); // TODO
            WSUrlFetch ws = new WSUrlFetch();
            WSRequest request = ws.newRequest(authUrl, "UTF-8");
            request.scheme = WS.Scheme.BASIC;
            
            JsonObject jsonResponse = request.get().getJson().getAsJsonObject();
            
            // Parse jsonResponse to get the acces_token, the refresh_token and the expires timeout.
            String accessToken = null;
            String refreshToken = null;
            Integer expires = null;
            accessToken = jsonResponse.get("access_token").getAsString();
            refreshToken = jsonResponse.get("refresh_token").getAsString();
            expires =  jsonResponse.get("expires_in").getAsInt();
            
            if (accessToken != null && expires != null) {
                try{
                    Class model = Class.forName("models.User"); // TODO
                    Method method = model.getMethod("findForM4FCityOAuth", new Class[] { M4FCityProfile.class });
                    if(Modifier.isStatic(method.getModifiers())){
                        String uri = String.format("%s?oauth_token=%s", getM4FCityUserInfoApiUrl(), accessToken);
                        
                        WSRequest req = ws.newRequest(uri, "UTF-8");
                        //req.headers.put("oauth_token", accessToken);
                        //request.scheme = WS.Scheme.BASIC; // TODO
                        
                        //request.oauthSecret = accessToken;
                        JsonObject jsonData = req.get().getJson().getAsJsonObject();
                        
                        //JsonObject jsonData = ws.newRequest(uri, "UTF-8").get().getJson().getAsJsonObject();
                        jsonData.add("accessToken", new JsonPrimitive(accessToken));
                        jsonData.add("expires", new JsonPrimitive(expires));
                       
                        method.invoke(null, new M4FCityProfile(jsonData));
                    }else{
                        throw new UnexpectedException("Module m4fcity expects your findForM4FCityOAuth method to be static");
                    }
                }catch(ClassNotFoundException e){
                    throw new UnexpectedException("Module m4fcity cannot find your model class "+getModel()); 
                }catch(NoSuchMethodException e){
                    throw new UnexpectedException("Module m4fcity requires that your model class "+getModel()+" must provide a method with this signature: [public static void findForM4FCityOAuth(M4FCityProfile data)]");
                }catch(IllegalAccessException e){
                    throw new UnexpectedException("Module m4fcity does not have access to call your model's findForM4FCityOAuth");
                }catch(InvocationTargetException e){
                    throw new UnexpectedException("Module m4fcity encountered an error while calling your model's findForM4FCityOAuth: "+e.getMessage());
                }
            } else {
                throw new UnexpectedException("Module m4fcity could not find access token and expires in m4fcity callback");
            }
        }
        redirect("/");
	}
	
	/**
	 * Gets the api key.
	 * 
	 * @return the api key
	 */
	public static String getApiKey() {
		return Play.configuration.getProperty("m4fcity.apiKey");
	}

	/**
	 * Gets the secret.
	 * 
	 * @return the secret
	 */
	public static String getSecret() {
		return Play.configuration.getProperty("m4fcity.secret");
	}

	/**
	 * Gets the model.
	 * 
	 * @return the model
	 */
	public static String getModel() {
		return Play.configuration.getProperty("m4fcity.model");
	}

	/**
	 * Gets the land url.
	 * 
	 * @return the land url
	 */
	public static String getLandUrl() {
		return Play.configuration.getProperty("m4fcity.landUrl");
	}
	
	/**
	 * Gets the m4fcity oauth authorize url.
	 * 
	 * @return the m4fcity oauth authorize url
	 */
	public static String getM4FCityOauthAuthorizeUrl() {
		return Play.configuration.getProperty("m4fcity.oauthAuthorizeUrl");
	}
	
	/**
	 * Gets the m4fcity oauth access token url.
	 * 
	 * @return the m4fcity oauth access token url
	 */
	public static String getM4FCityOauthAccessTokenUrl() {
		return Play.configuration.getProperty("m4fcity.oauthAccessTokenUrl");
	}
	
	/**
	 * Gets the m4fcity user info api url.
	 * 
	 * @return the m4fcity user info api url
	 */
	public static String getM4FCityUserInfoApiUrl() {
		return Play.configuration.getProperty("m4fcity.userInfoApiUrl");
	}
	
	/**
	 * Gets the oauth redirect url from the Router.
	 * 
	 * @return the oauth redirect url
	 */
	public static String getOauthRedirectUrl() {
		return Router.getFullUrl("M4FCityController.callback");
	}
	
	public static String getAuthUrl(String authCode) {
		 return String.format("%s?client_id=%s&redirect_uri=%s&client_secret=%s&code=%s&grant_type=authorization_code&scope", getM4FCityOauthAccessTokenUrl(), WS.encode(getApiKey()), WS.encode(getOauthRedirectUrl()), WS.encode(getSecret()), WS.encode(authCode));
	}
}
